package edu.zju.gis.dbfg.queryserver.service.impl;

import edu.zju.gis.dbfg.queryserver.model.SearchResult;
import edu.zju.gis.dbfg.queryserver.service.SpatialQueryService;
import edu.zju.gis.dbfg.queryserver.tool.FormatUtil;
import edu.zju.gis.dbfg.queryserver.tool.RestElasticSearchHelper;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.geo.builders.*;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static edu.zju.gis.dbfg.queryserver.constant.LimitedFieldsName.SPATIAL_FIELD_NAME;

@Slf4j
@Service
public class SpatialQueryServiceImp implements SpatialQueryService {

    @Autowired
    private RestElasticSearchHelper helper;

    public SearchResult selectByPoint(String indexName, double lon, double lat) {
        GeoShapeQueryBuilder geoQb = null;
        try {
            ShapeBuilder builder = new PointBuilder(lon, lat);
            geoQb = QueryBuilders.geoShapeQuery(SPATIAL_FIELD_NAME, builder);
        } catch (Exception e) {
            System.out.println("Point Build ERROR.");
            return null;
        }
        return selectByShape(indexName, geoQb);
    }

    public SearchResult selectByPolygon(String indexName, List<double[]> points) {
        GeoShapeQueryBuilder geoQb = null;
        try {
            CoordinatesBuilder corsBuilder = new CoordinatesBuilder();
            List<Coordinate> cors = new ArrayList<>();
            if (points.size() > 3) {
                for (int i = 0; i < points.size(); i++) {
                    cors.add(new Coordinate(points.get(i)[0], points.get(i)[1]));
                }
            } else if (points.size() == 2) {
                double minX = points.get(0)[0] < points.get(1)[0] ? points.get(0)[0] : points.get(1)[0];
                double maxX = points.get(0)[0] > points.get(1)[0] ? points.get(0)[0] : points.get(1)[0];
                double minY = points.get(0)[1] < points.get(1)[1] ? points.get(0)[1] : points.get(1)[1];
                double maxY = points.get(0)[1] > points.get(1)[1] ? points.get(0)[1] : points.get(1)[1];
                cors.add(new Coordinate(minX,minY));
                cors.add(new Coordinate(maxX,minY));
                cors.add(new Coordinate(maxX,maxY));
                cors.add(new Coordinate(minX,maxY));
                cors.add(new Coordinate(minX,minY));
            }
            corsBuilder.coordinates(cors);
            ShapeBuilder builder = new PolygonBuilder(corsBuilder);
            geoQb = QueryBuilders.geoShapeQuery(SPATIAL_FIELD_NAME, builder);
        } catch (Exception e) {
            System.out.println("Point Build ERROR.");
            System.out.println(e);
            return null;
        }
        return selectByShape(indexName, geoQb);
    }

    public SearchResult selectByPointWithScroll(String indexName, double lon, double lat, String scrollId) {
        return null;
    }

    public SearchResult selectByPolygonWithScroll(String indexName, List<double[]> points, String scrollId) {
        return null;
    }

    private SearchResult selectByShape(String indexName, GeoShapeQueryBuilder geoQb) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //TODO 图上空间查询，限制其查询条数为100，应该将size设置迁移至controller中
        searchSourceBuilder.size(100);
        searchSourceBuilder.query(geoQb);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);
        searchRequest.source(searchSourceBuilder);
        return FormatUtil.SearchResponse2Result(helper.search(searchRequest));
    }
}
